/* file: scan.c  */

#include "public.h"
#include "tools.h"
#include "scan.h"

char token_str[TOKENLEN+1];

static int line_pos = 0;
static int cur_size = 0;
#define BUFFERLEN 1024
static char buffer[BUFFERLEN+1];

typedef enum
{
    START, SA, DUPCOM, EXITCOM,
    DUPNUM, DUPID, SB, SC, SD, PA,
    DONE
}state_type;

static struct
{
    char *str;
    token_type toke;
}r_word[] = {

{"if",IF}, {"else", ELSE}, {"int", INT}, 
{"void", VOID}, {"while", WHILE}, {"main", MAIN},
{"in", IN}, {"out", OUT}

};

static char get_char(void)
{
    if(line_pos >= cur_size)
    {
		++lineno;
		if(fgets(buffer, BUFFERLEN-1, src))
		{
		    cur_size = strlen(buffer);
			line_pos = 0;
			return buffer[line_pos++];
		}
		else return EOF;
    } 
	else return buffer[line_pos++];
}

static void un_getchar(void)
{
    line_pos--;
}

static token_type find_reserve(char *s)
{
    int i;
	for(i = 0; i < 8; i++)
		if(strcmp(s, r_word[i].str) == 0)
			return r_word[i].toke;
	return ID;
}

token_type get_token(void)
{
    int index = 0;
	token_type cur_token;
	state_type state = START;                      /*  auto machine's state           */
	int save;                                      /*  wether save the current token  */
	char looknext;
	while(state != DONE)
	{
	    char c = get_char();
		save = true;
		switch(state)
		{
		    case START:
				if(is_digit(c))
					state = DUPNUM;
				else if(is_alpha(c))
					state = DUPID;
				else if(c == '<')
					state = SB;
				else if(c == '>')
					state = SC;
				else if(c == '=')
					state = SD;
				else if(c == '!')
					state = PA;
				else if((c == ' ')||(c == '\t')||(c == '\n'))
					save = false;                     /* skip blank   */
				else if(c == '/')
				{
				    looknext = get_char();
					if(looknext == '*')
						save = false;                 /* skip comment */
					un_getchar();
					state = SA;
				}
				else 
				{
				    state = DONE;
					switch(c)
					{
					    case EOF: 
							save = false;
							cur_token = ENDFILE;
							break;
						case '+':
							cur_token = PLUS;
							break;
						case '-':
							cur_token = MINUS;
							break;
						case '*':
							cur_token = TIMES;
							break;
						case ';':
							cur_token = SEMI;
							break;
						case ',':
							cur_token = COLO;
							break;
						case '(':
							cur_token = LPARE;
							break;
						case ')':
							cur_token = RPARE;
							break;
						case '{':
							cur_token = LBRA;
							break;
						case '}':
							cur_token = RBRA;
							break;
						default:
							cur_token = ERROR;
							break;

					}
				}
				break;
			    case SA:
					save = false;
					if(c == '*')
						state = DUPCOM;
					else 
					{
					    state = DONE;
						un_getchar();
						cur_token = DIVI;
					}
					break;
				case DUPCOM:
					save = false;
					if(c == '*')
						state = EXITCOM;
					break;
				case EXITCOM:
					save = false;
					if(c == '/')
						state = START;
					else if(c != '*')
						state = DUPCOM;
					break;
				case DUPNUM:
					if(!is_digit(c))
					{
					    un_getchar();
						save = false;
						state = DONE;
						cur_token = NUM;
					}
					break;
				case DUPID:
					if(!is_alpha(c))
					{
					    un_getchar();
						save = false;
						state = DONE;
						cur_token = ID;
					}
					break;
				case SB:
					state = DONE;
					if(c == '=')
					    cur_token = LE;
					else 
					{
					   un_getchar();
					   save = false;
					   cur_token = LS;
					}
					break;
				case SC:
					state = DONE;
					if(c == '=')
						cur_token = GE;
					else
					{
					    un_getchar();
						save = false;
						cur_token = GT;
					}
					break;
				case SD:
					state = DONE;
					if(c == '=')
						cur_token = EQ;
					else 
					{
					    un_getchar();
						save = false;
						cur_token = ASSIGN;
					}
					break;
				case PA:
					state = DONE;
					if(c == '=')
						cur_token = NE;
					else 
					{
					   un_getchar();
					   save = false;
					   cur_token = ERROR;
					}
					break;
				case DONE:
				default:
					state = DONE;
					cur_token = ERROR;
                    break;
		}
		
		if(save)                        /*  save current token */
			token_str[index++] = c;
		if(state == DONE)
		{
			token_str[index] = '\0';
			if(cur_token == ID)
				cur_token = find_reserve(token_str);
		}
	}
	fprintf(my_test, "%3d  ", lineno);
	print_token(cur_token, token_str);


    return cur_token;
}
